<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>AI股票监控与预测决策平台</title>
  <meta name="viewport" content="width=3840, initial-scale=1.0">
  <style>
    :root {
      --bg: #171b22;
      --panel: #232835;
      --panel-light: #252a39;
      --accent: #14c684;
      --accent2: #e04e39;
      --accent-warn: #f9c846;
      --text: #f5f7fa;
      --subtext: #b9bfcf;
      --border: #2a2f3c;
      --up: #16e79a;
      --down: #fd5061;
      --neutral: #50546b;
      --heat-high: #ff2d55;
      --heat-mid: #f9c846;
      --heat-low: #16e79a;
      --k-green: #12c783;
      --k-red: #e04e39;
      --scrollbar: #232835;
    }
    html, body {
      margin: 0;
      padding: 0;
      width: 100vw;
      height: 100vh;
      background: var(--bg);
      color: var(--text);
      font-family: 'Segoe UI', 'PingFang SC', 'Arial', sans-serif;
      user-select: none;
      overflow: hidden;
    }
    /* Layout */
    #app {
      width: 100vw;
      height: 100vh;
      display: flex;
      flex-direction: column;
      background: var(--bg);
    }
    header {
      height: 96px;
      min-height: 96px;
      background: var(--panel);
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 0 64px;
      border-bottom: 2px solid var(--border);
      font-size: 38px;
      letter-spacing: 2px;
    }
    .logo {
      font-weight: bold;
      color: var(--accent);
      display: flex;
      align-items: center;
      font-size: 44px;
    }
    .logo svg {
      width: 56px;
      height: 56px;
      margin-right: 18px;
    }
    .header-info {
      display: flex;
      align-items: center;
      gap: 54px;
      font-size: 32px;
    }
    .market-status {
      padding: 8px 28px;
      border-radius: 16px;
      font-weight: bold;
      margin-left: 18px;
      background: var(--panel-light);
      border: 1.5px solid var(--accent2);
      color: var(--accent2);
      font-size: 28px;
    }
    main {
      flex: 1;
      display: flex;
      height: calc(100vh - 96px - 110px);
      min-height: 0;
      /* 110px: footer */
    }
    /* Sidebar: 数据汇总与AI洞察 */
    .sidebar {
      width: 620px;
      min-width: 620px;
      background: var(--panel);
      display: flex;
      flex-direction: column;
      border-right: 2px solid var(--border);
      padding: 32px 24px 24px 24px;
      box-sizing: border-box;
      gap: 34px;
      overflow-y: auto;
    }
    /* 主内容区 */
    .content {
      flex: 1;
      display: flex;
      flex-direction: column;
      gap: 24px;
      padding: 24px 24px 24px 24px;
      overflow: hidden;
      box-sizing: border-box;
      min-width: 0;
    }
    .top-row {
      display: flex;
      flex: 1 1 0;
      gap: 24px;
      min-width: 0;
    }
    .module {
      background: var(--panel-light);
      border-radius: 22px;
      box-shadow: 0 2px 16px 0 #0005;
      padding: 22px 26px 16px 26px;
      display: flex;
      flex-direction: column;
      box-sizing: border-box;
      min-width: 0;
      position: relative;
    }
    .module-title {
      font-size: 28px;
      font-weight: 600;
      color: var(--accent);
      margin-bottom: 14px;
      letter-spacing: 1.5px;
    }
    .module-toolbar {
      position: absolute;
      top: 20px;
      right: 26px;
      display: flex;
      gap: 12px;
      z-index: 2;
    }
    .toolbar-btn {
      background: var(--border);
      border: none;
      color: var(--text);
      font-size: 20px;
      padding: 4px 16px;
      border-radius: 10px;
      cursor: pointer;
      transition: background 0.2s;
    }
    .toolbar-btn:hover {
      background: var(--accent2);
      color: #fff;
    }
    /* 滚动条美化 */
    ::-webkit-scrollbar {
      width: 10px;
      background: var(--scrollbar);
      border-radius: 6px;
    }
    ::-webkit-scrollbar-thumb {
      background: var(--border);
      border-radius: 6px;
    }
    /* 热力图 */
    #heatmap-canvas {
      width: 100%;
      height: 320px;
      border-radius: 15px;
      background: #181c2b;
      margin-top: 10px;
      cursor: pointer;
    }
    .heatmap-legend {
      display: flex;
      align-items: center;
      margin-top: 8px;
      gap: 16px;
      font-size: 18px;
      color: var(--subtext);
    }
    .heatmap-legend-bar {
      width: 120px;
      height: 12px;
      border-radius: 8px;
      background: linear-gradient(90deg, var(--heat-low), var(--heat-mid), var(--heat-high));
    }
    /* 股票实时监控 */
    #realtime-canvas {
      width: 100%;
      height: 170px;
      margin-bottom: 8px;
      border-radius: 11px;
      background: #191d27;
    }
    /* 重大新闻/事件流 */
    .news-list {
      max-height: 300px;
      overflow: auto;
      display: flex;
      flex-direction: column;
      gap: 14px;
      margin-top: 12px;
    }
    .news-item {
      background: #23283a;
      border-left: 5px solid var(--accent);
      padding: 12px 18px 12px 20px;
      border-radius: 12px;
      color: var(--text);
      font-size: 20px;
      display: flex;
      align-items: flex-start;
      gap: 18px;
      position: relative;
      transition: border-color 0.18s;
    }
    .news-item[data-type="利好"] { border-left-color: var(--up); }
    .news-item[data-type="利空"] { border-left-color: var(--down); }
    .news-item[data-type="突发"] { border-left-color: var(--accent2); }
    .news-meta {
      font-size: 16px;
      color: var(--subtext);
      margin-top: 6px;
    }
    /* 投资组合表现 */
    #portfolio-canvas {
      width: 100%;
      height: 160px;
      background: #191d27;
      border-radius: 13px;
    }
    .portfolio-stats {
      display: flex;
      gap: 34px;
      font-size: 20px;
      margin-top: 10px;
    }
    .portfolio-stat {
      color: var(--subtext);
    }
    .portfolio-stat span {
      color: var(--accent);
      font-weight: bold;
      margin-left: 6px;
    }
    /* 自选股仪表盘 */
    .watchlist-table {
      width: 100%;
      margin-top: 6px;
      border-collapse: collapse;
      background: #191d27;
      border-radius: 10px;
      overflow: hidden;
    }
    .watchlist-table th, .watchlist-table td {
      padding: 10px 12px;
      text-align: center;
      font-size: 20px;
    }
    .watchlist-table th {
      color: var(--accent);
      background: #202434;
    }
    .watchlist-table tr {
      background: none;
      border-bottom: 1.5px solid var(--border);
    }
    .watchlist-table td {
      color: var(--text);
    }
    .watchlist-table td.up { color: var(--up); }
    .watchlist-table td.down { color: var(--down); }
    .watchlist-table td.warn { color: var(--accent-warn); }
    .watchlist-table td.normal { color: var(--text); }
    .ai-rating {
      font-weight: bold;
      padding: 2px 8px;
      border-radius: 7px;
      font-size: 18px;
      display: inline-block;
    }
    .ai-rating.buy { background: #133e2a; color: var(--up);}
    .ai-rating.hold { background: #39391b; color: var(--heat-mid);}
    .ai-rating.sell { background: #3e1b1b; color: var(--down);}
    .ai-warning { background: var(--accent2); color: #fff; border-radius: 7px; padding: 2px 8px; }
    /* 股价/成交量趋势图 */
    #trend-canvas {
      width: 100%;
      height: 260px;
      background: #181c2b;
      border-radius: 16px;
      margin-top: 12px;
    }
    .trend-toolbar {
      display: flex;
      align-items: center;
      gap: 10px;
      margin-bottom: 10px;
    }
    .trend-toolbar select, .trend-toolbar input {
      background: var(--panel-light);
      color: var(--text);
      border: 1px solid var(--border);
      border-radius: 7px;
      font-size: 18px;
      padding: 3px 8px;
      outline: none;
      margin-right: 8px;
    }
    .trend-toolbar label { color: var(--subtext); font-size: 18px; }
    /* AI预测面板 */
    #ai-predict-canvas {
      width: 100%;
      height: 170px;
      background: #181c2b;
      border-radius: 13px;
    }
    .ai-predict-toolbar {
      display: flex;
      align-items: center;
      gap: 12px;
      margin-bottom: 6px;
    }
    /* AI风险预警 */
    .ai-alerts {
      margin-top: 10px;
      display: flex;
      flex-direction: column;
      gap: 10px;
    }
    .ai-alert {
      padding: 12px 18px;
      border-radius: 9px;
      font-size: 19px;
      font-weight: 500;
      display: flex;
      align-items: center;
      gap: 18px;
    }
    .ai-alert.critical { background: #3c1e1e; color: var(--down); border-left: 5px solid var(--down);}
    .ai-alert.warn { background: #3c371e; color: var(--accent-warn); border-left: 5px solid var(--accent-warn);}
    .ai-alert.info { background: #1e3c2e; color: var(--up); border-left: 5px solid var(--up);}
    /* KPI对比 */
    #kpi-canvas {
      width: 100%;
      height: 110px;
      background: #191d27;
      border-radius: 9px;
      margin-top: 8px;
    }
    .kpi-toolbar {
      display: flex;
      gap: 9px;
      align-items: center;
      margin-bottom: 4px;
    }
    .kpi-toolbar select {
      background: var(--panel-light);
      color: var(--text);
      border: 1px solid var(--border);
      border-radius: 6px;
      font-size: 17px;
      padding: 2px 8px;
      outline: none;
    }
    /* 操作区 */
    .footer {
      height: 110px;
      background: var(--panel);
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 0 50px;
      border-top: 2px solid var(--border);
    }
    .footer-actions {
      display: flex;
      align-items: center;
      gap: 28px;
    }
    .stock-search-bar {
      display: flex;
      align-items: center;
      gap: 12px;
      background: var(--panel-light);
      padding: 8px 16px;
      border-radius: 10px;
    }
    .stock-search-bar input {
      background: transparent;
      border: none;
      color: var(--text);
      font-size: 23px;
      outline: none;
      width: 220px;
    }
    .footer-btn {
      background: var(--accent2);
      color: #fff;
      border: none;
      font-size: 22px;
      border-radius: 10px;
      padding: 10px 28px;
      font-weight: 600;
      cursor: pointer;
      transition: background 0.2s;
    }
    .footer-btn:hover {
      background: var(--accent);
      color: #111;
    }
    .refresh-settings {
      display: flex;
      align-items: center;
      gap: 12px;
      color: var(--subtext);
      font-size: 20px;
    }
    .refresh-settings input {
      width: 62px;
      font-size: 20px;
      border-radius: 6px;
      background: var(--panel-light);
      color: var(--text);
      border: 1px solid var(--border);
      padding: 2px 5px;
      outline: none;
      margin-left: 7px;
    }
    /* 响应式适配 */
    @media (max-width: 1900px) {
      .sidebar { width: 420px; min-width: 420px; }
      .module-title { font-size: 22px;}
    }
    @media (max-width: 1400px) {
      .sidebar { width: 240px; min-width: 240px; }
      .module-title { font-size: 16px;}
      .header-info { font-size: 18px;}
    }
  </style>
</head>
<body>
<div id="app">
  <!-- 页眉 -->
  <header>
    <div class="logo">
      <svg viewBox="0 0 64 64">
        <rect x="6" y="36" width="12" height="18" rx="3" fill="#16e79a"/>
        <rect x="24" y="24" width="12" height="30" rx="3" fill="#f9c846"/>
        <rect x="42" y="10" width="12" height="44" rx="3" fill="#e04e39"/>
      </svg>
      AI股市监控平台
    </div>
    <div class="header-info">
      <span id="header-time">--:--:--</span>
      <span class="market-status" id="market-status">收盘</span>
    </div>
  </header>
  <main>
    <!-- 侧边栏：数据汇总/AI洞察 -->
    <aside class="sidebar">
      <div class="module">
        <div class="module-title">投资组合表现</div>
        <canvas id="portfolio-canvas"></canvas>
        <div class="portfolio-stats" id="portfolio-stats"></div>
      </div>
      <div class="module">
        <div class="module-title">AI洞察与风险预警</div>
        <div class="ai-alerts" id="ai-alerts"></div>
      </div>
      <div class="module">
        <div class="module-title">KPI对比/变化</div>
        <div class="kpi-toolbar">
          <label>指标：</label>
          <select id="kpi-select">
            <option value="return">收益率</option>
            <option value="sharpe">夏普比率</option>
            <option value="excess">超额收益</option>
          </select>
        </div>
        <canvas id="kpi-canvas"></canvas>
      </div>
    </aside>
    <!-- 主内容区 -->
    <section class="content">
      <div class="top-row">
        <div class="module" style="flex:2.5;">
          <div class="module-title">市场热力图</div>
          <canvas id="heatmap-canvas"></canvas>
          <div class="heatmap-legend">
            <span>跌幅</span>
            <div class="heatmap-legend-bar"></div>
            <span>涨幅</span>
          </div>
        </div>
        <div class="module" style="flex:1.5;">
          <div class="module-title">关键股票实时监控</div>
          <canvas id="realtime-canvas"></canvas>
          <div id="realtime-stats"></div>
        </div>
        <div class="module" style="flex:2;">
          <div class="module-title">重大事件/新闻</div>
          <div class="news-list" id="news-list"></div>
        </div>
      </div>
      <div class="top-row" style="flex: 1.2 1 0;">
        <div class="module" style="flex:1.6;">
          <div class="module-title">股价/成交量趋势图</div>
          <div class="trend-toolbar">
            <select id="trend-symbol"></select>
            <select id="trend-period">
              <option value="1m">分时</option>
              <option value="5m">5分钟</option>
              <option value="1h">1小时</option>
              <option value="1d">日K</option>
            </select>
            <label>指标:</label>
            <label><input type="checkbox" checked id="ma-ind"/>MA</label>
            <label><input type="checkbox" checked id="macd-ind"/>MACD</label>
            <label><input type="checkbox" checked id="rsi-ind"/>RSI</label>
          </div>
          <canvas id="trend-canvas"></canvas>
        </div>
        <div class="module" style="flex:1;">
          <div class="module-title">AI预测分析</div>
          <div class="ai-predict-toolbar">
            <select id="ai-predict-symbol"></select>
            <select id="ai-predict-horizon">
              <option value="1h">1小时</option>
              <option value="1d" selected>1天</option>
              <option value="1w">1周</option>
            </select>
          </div>
          <canvas id="ai-predict-canvas"></canvas>
        </div>
        <div class="module" style="flex:1.2;">
          <div class="module-title">自选股仪表盘</div>
          <table class="watchlist-table" id="watchlist-table"></table>
        </div>
      </div>
    </section>
  </main>
  <!-- 操作区 -->
  <footer class="footer">
    <div class="footer-actions">
      <div class="stock-search-bar">
        <span>股票代码:</span>
        <input id="stock-search" type="text" placeholder="输入股票代码/名称…" autocomplete="off"/>
        <button class="footer-btn" id="search-btn">搜索</button>
      </div>
      <button class="footer-btn" id="add-watch-btn">加入自选</button>
    </div>
    <div class="refresh-settings">
      刷新频率:
      <input id="refresh-interval" type="number" min="1" max="60" step="1" value="5"/>秒
      <button class="footer-btn" id="refresh-btn">立即刷新</button>
    </div>
  </footer>
</div>
<script>
/*
==== 模拟数据生成 ====
所有行情、预测、AI分析均为模拟
*/
const symbols = [
  { code: '600519', name: '贵州茅台', sector: '白酒', color: '#ffb84d' },
  { code: '000858', name: '五粮液', sector: '白酒', color: '#ff9c4d' },
  { code: '601318', name: '中国平安', sector: '保险', color: '#87e8de' },
  { code: '300750', name: '宁德时代', sector: '锂电池', color: '#56aaff' },
  { code: '601888', name: '中国中免', sector: '免税', color: '#cfa8ff' },
  { code: '002594', name: '比亚迪', sector: '新能源', color: '#1be9b3' },
  { code: '600036', name: '招商银行', sector: '银行', color: '#ff7b7b' },
  { code: '000333', name: '美的集团', sector: '家电', color: '#fdc2e9' },
  { code: '600690', name: '海尔智家', sector: '家电', color: '#5e6fff' },
  { code: '601166', name: '兴业银行', sector: '银行', color: '#e8e7a6' }
];
const sectors = [...new Set(symbols.map(s=>s.sector))];

// 生成热力图数据
function generateHeatmapData() {
  // 行业/板块，每个板块若干个股
  const data = [];
  sectors.forEach((sector, i) => {
    // 每板块2-3只
    const stocks = symbols.filter(s => s.sector === sector);
    stocks.forEach(stock => {
      // 涨跌幅 -7%~+8%，成交活跃度0~1
      let change = +(Math.random() * 15 - 7).toFixed(2);
      let vol = +(Math.random()).toFixed(2);
      // 异常点：个别闪崩/暴涨
      if (Math.random() < 0.12) change = +(Math.random() > 0.5 ? 13 + Math.random()*5 : -10-Math.random()*5).toFixed(2);
      data.push({
        code: stock.code,
        name: stock.name,
        sector: sector,
        change,
        vol,
      });
    });
  });
  return data;
}
// 生成分时/日K数据
function generateTrendData(symbol, period='1d', n=80) {
  // 以某价格为基准，波动生成
  let base = 100 + Math.random()*50;
  if(symbol==='600519') base=1650;
  if(symbol==='000858') base=140;
  if(symbol==='300750') base=200;
  if(symbol==='601318') base=60;
  // 根据period决定波动幅度
  let points = [];
  let last = base;
  for(let i=0;i<n;i++){
    let noise = (Math.random()-0.5) * (period==='1d'? 3 : period==='1h'? 1.2 : 0.3);
    let open = last + noise;
    let close = open + (Math.random()-0.5)*(period==='1d'?3:period==='1h'?1.7:0.5);
    let high = Math.max(open,close)+(Math.random())*(period==='1d'?2.2:0.8);
    let low = Math.min(open,close)-(Math.random())*(period==='1d'?2.2:0.8);
    let vol = Math.round(2000 + Math.random()*6000 + (close-base)*3);
    // 闪崩/拉升异常点
    if(Math.random()<0.025 && i>5){
      if(Math.random()>0.5) close+=Math.random()*22;
      else close-=Math.random()*22;
    }
    last = close;
    points.push({open,close,high,low,vol});
  }
  return points;
}
// 生成实时盘口/技术指标
function generateRealtimeStats(symbol) {
  // 取K线最后一根为现价
  const trend = trendData[symbol] || generateTrendData(symbol);
  const last = trend[trend.length-1];
  // 随机生成
  const bid = +(last.close - Math.random()*1.2).toFixed(2);
  const ask = +(last.close + Math.random()*1.2).toFixed(2);
  const buy1 = Math.round(Math.random()*1000+300);
  const sell1 = Math.round(Math.random()*900+200);
  // MA等
  const ma5 = +(trend.slice(-5).map(x=>x.close).reduce((a,b)=>a+b,0)/5).toFixed(2);
  const ma10 = +(trend.slice(-10).map(x=>x.close).reduce((a,b)=>a+b,0)/10).toFixed(2);
  // MACD简化
  const dif = +(ma5 - ma10).toFixed(2);
  // RSI
  let up=0,down=0;
  for(let i=trend.length-7;i<trend.length-1;i++) {
    let v = trend[i+1].close-trend[i].close;
    if(v>0) up+=v; else down-=v;
  }
  const rsi = +(up/(up+down)*100).toFixed(1);
  return {
    price: last.close.toFixed(2),
    high: last.high.toFixed(2),
    low: last.low.toFixed(2),
    vol: last.vol,
    bid, ask, buy1, sell1,
    ma5, ma10, dif, rsi
  };
}
// 生成AI预测面板数据
function generateAIPredict(symbol, horizon='1d') {
  // 以当前为基准，预测未来涨跌概率区间
  const now = trendData[symbol][trendData[symbol].length-1].close;
  let range = {low: now-8, high: now+9, probUp: Math.random()*0.7+0.15};
  if(horizon==='1w') range.low-=9, range.high+=10;
  if(horizon==='1h') range.low+=-2,range.high+=2;
  // 异常点
  if(Math.random()<0.1) {
    if(Math.random()>0.5) range.high+=Math.random()*12, range.probUp+=0.14;
    else range.low-=Math.random()*12, range.probUp-=0.13;
  }
  if(range.probUp>1) range.probUp=1;
  if(range.probUp<0) range.probUp=0.02;
  return {now, ...range};
}
// 生成AI风险预警
function generateAIAlerts() {
  // 随机生成价格异动、突发新闻等预警
  let alerts = [];
  if(Math.random()<0.32) alerts.push({
    level: 'critical', msg: `【${pick(symbols).name}】价格闪崩，30分钟内跌幅超过8%`
  });
  if(Math.random()<0.26) alerts.push({
    level: 'warn', msg: `【${pick(symbols).name}】成交量异常放大，建议关注资金异动`
  });
  if(Math.random()<0.18) alerts.push({
    level: 'info', msg: `【${pick(symbols).name}】多项技术指标出现金叉，AI建议关注`
  });
  return alerts;
}
// 生成投资组合表现
function generatePortfolio() {
  // 资产配置，盈亏，收益率，风险
  const allocation = [
    {name:'白酒', percent:32, color:'#ffb84d'},
    {name:'新能源', percent:22, color:'#1be9b3'},
    {name:'银行', percent:18, color:'#e8e7a6'},
    {name:'保险', percent:10, color:'#87e8de'},
    {name:'家电', percent:18, color:'#5e6fff'}
  ];
  // 盈亏：分板块
  const profit = allocation.map(a => ({
    name: a.name,
    pnl: +(Math.random()*12-4).toFixed(2)
  }));
  // 总收益率
  const totalReturn = +(profit.reduce((s,p)=>s+p.pnl,0)).toFixed(2);
  // Beta, VaR
  const beta = +(Math.random()*0.3+0.7).toFixed(2);
  const varRisk = +(Math.random()*5+3).toFixed(2);
  return {allocation, profit, totalReturn, beta, varRisk};
}
// 生成KPI对比变化
function generateKPIData(type='return') {
  // 近12期
  let val = [];
  for(let i=0;i<13;i++){
    if(type==='return') val.push(+(Math.random()*8-2).toFixed(2));
    else if(type==='sharpe') val.push(+(Math.random()*1.2+0.7).toFixed(2));
    else val.push(+(Math.random()*6-1.2).toFixed(2));
  }
  return val;
}
// 生成新闻事件流
function generateNewsList() {
  const types = ['突发','利好','利空'];
  const pool = [
    {type:'突发', title:'【突发】美联储意外加息25基点', time:'14:05', detail:'美元汇率大幅波动，全球市场避险情绪升温。'},
    {type:'利好', title:'贵州茅台财报超预期', time:'13:32', detail:'公司Q1净利润同比增长23%，白酒板块普涨。'},
    {type:'利空', title:'宁德时代遭遇大宗减持', time:'13:10', detail:'大股东累计减持3%，盘中跳水，锂电池板块承压。'},
    {type:'突发', title:'比亚迪新车型获欧洲五星安全认证', time:'12:50', detail:'新能源车出口预期提升，汽车股集体拉升。'},
    {type:'利好', title:'中国平安获准设立消费金融公司', time:'12:18', detail:'金融板块午后走强，券商股跟涨。'},
    {type:'利空', title:'美的集团一季度业绩低于预期', time:'12:00', detail:'家电板块短线承压，部分资金流出。'},
    {type:'突发', title:'海尔智家拟回购股份', time:'11:45', detail:'家电板块活跃，回购金额创历史新高。'}
  ];
  // 随机挑选+乱序
  return shuffle(pool).slice(0,5);
}
// 生成自选股数据
function generateWatchlistData() {
  // 默认关注
  const list = [
    {code:'600519', name:'贵州茅台'},
    {code:'300750', name:'宁德时代'},
    {code:'002594', name:'比亚迪'},
    {code:'601318', name:'中国平安'},
    {code:'601888', name:'中国中免'}
  ];
  return list.map(item=>{
    let t = trendData[item.code]||generateTrendData(item.code);
    let price = t[t.length-1].close;
    let chg = +(Math.random()*8-3).toFixed(2);
    let ai = Math.random();
    let warn = Math.random()<0.18 ? '警告' : '';
    let rating = ai>0.7?'buy':ai>0.4?'hold':'sell';
    let pe = +(Math.random()*30+5).toFixed(1);
    let pb = +(Math.random()*4+0.6).toFixed(2);
    return {
      ...item,
      price: price.toFixed(2),
      chg,
      pe, pb, ai_rating: rating,
      warn
    };
  });
}
/* 工具函数 */
function pick(arr){ return arr[Math.floor(Math.random()*arr.length)]; }
function shuffle(arr) {
  let a = arr.slice();
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}
/* ==== 全局数据 ==== */
let heatmapData = generateHeatmapData();
let trendData = {};
symbols.forEach(s => trendData[s.code] = generateTrendData(s.code));
let newsData = generateNewsList();
let portfolioData = generatePortfolio();
let kpiType = "return";
let kpiData = generateKPIData(kpiType);
let aiAlerts = generateAIAlerts();
let watchlistData = generateWatchlistData();
let aiPredictData = {};
/* ==== 实时刷新机制 ==== */
let refreshInterval = 5; // 秒
let refreshTimer = null;
/* ==== 组件渲染 ==== */
/* 页眉时间和市场状态 */
function updateHeader() {
  const el = document.getElementById('header-time');
  const now = new Date();
  el.textContent = now.toLocaleTimeString('zh-CN', {hour12:false});
  let hour = now.getHours();
  let status = '收盘';
  if(hour>=9 && hour<11 || hour>=13 && hour<15) status = '交易中';
  else if(hour===11 || hour===12) status = '午休';
  else if(hour>=7 && hour<9 || hour>=15 && hour<16) status = '盘前/盘后';
  document.getElementById('market-status').textContent = status;
  document.getElementById('market-status').style.borderColor = status==='交易中'? 'var(--accent)':
    status==='盘前/盘后'? 'var(--accent-warn)':'var(--accent2)';
  document.getElementById('market-status').style.color = status==='交易中'? 'var(--accent)':
    status==='盘前/盘后'? 'var(--accent-warn)':'var(--accent2)';
}
/* 热力图 */
function renderHeatmap() {
  const canvas = document.getElementById('heatmap-canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;
  ctx.clearRect(0,0,canvas.width,canvas.height);
  // 行业分区，矩形分布
  const M = heatmapData.length;
  const rows = sectors.length;
  const cols = Math.ceil(M/rows);
  const cellW = (canvas.width-60)/(cols);
  const cellH = (canvas.height-30)/(rows);
  let i=0;
  for(let r=0;r<rows;r++){
    let y = 20 + r*cellH;
    for(let c=0;c<cols;c++){
      if(i>=M) break;
      let stock = heatmapData[i];
      let x = 30 + c*cellW;
      // 涨跌幅->色彩映射
      let pct = Math.max(-12, Math.min(15, stock.change));
      let color = getHeatColor(pct/15);
      // 高亮异常（如闪崩/暴涨）
      if(Math.abs(stock.change)>10) color = pct>0? "#fff176":"#f44336";
      ctx.fillStyle = color;
      ctx.globalAlpha = 0.88;
      ctx.fillRect(x, y, cellW-18, cellH-14);
      ctx.globalAlpha = 1;
      ctx.strokeStyle = "#222";
      ctx.strokeRect(x, y, cellW-18, cellH-14);
      ctx.fillStyle = "#fff";
      ctx.font = "bold 26px Segoe UI";
      ctx.fillText(stock.name, x+12, y+38);
      ctx.font = "20px Segoe UI";
      ctx.fillStyle = "#d3e0ff";
      ctx.fillText(stock.code, x+12, y+68);
      ctx.font = "bold 24px Segoe UI";
      ctx.fillStyle = pct>=0? "var(--up)":"var(--down)";
      ctx.fillText((pct>0?"+":"")+pct.toFixed(2)+"%", x+cellW/2-26, y+38);
      i++;
    }
  }
}
function getHeatColor(t) {
  // t -1~1, -1:大跌, 1:大涨
  if(t<-0.2) return "#fd5061";
  if(t<0) return "#ffa39e";
  if(t<0.5) return "#16e79a";
  if(t<0.85) return "#f9c846";
  return "#ff2d55";
}
/* 实时监控 */
function renderRealtime() {
  const canvas = document.getElementById('realtime-canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;
  ctx.clearRect(0,0,canvas.width,canvas.height);
  // 以第一个symbol为例
  let s = symbols[0].code;
  let t = trendData[s];
  let stats = generateRealtimeStats(s);
  // 画分时线
  ctx.save();
  ctx.translate(60,30);
  ctx.strokeStyle = "#6ee7b7";
  ctx.lineWidth = 2.5;
  ctx.beginPath();
  let N = t.length;
  let min = Math.min(...t.map(x=>x.low)), max = Math.max(...t.map(x=>x.high));
  for(let i=0;i<N;i++){
    let y = (1-(t[i].close-min)/(max-min))*(canvas.height-60);
    let x = (i/(N-1))*(canvas.width-120);
    if(i===0) ctx.moveTo(x,y);
    else ctx.lineTo(x,y);
  }
  ctx.stroke();
  ctx.restore();
  // 股价信息
  let html = `<span style="font-size:33px;font-weight:bold;color:var(--accent);">${symbols[0].name}（${symbols[0].code}）</span>
    <br>现价 <span style="color:var(--accent2);font-size:28px;">${stats.price}</span>
    高 <span style="color:var(--up);">${stats.high}</span>
    低 <span style="color:var(--down);">${stats.low}</span>
    <br>买一 ${stats.bid} (${stats.buy1}) / 卖一 ${stats.ask} (${stats.sell1})<br>
    MA5:${stats.ma5} MA10:${stats.ma10} DIF:${stats.dif} RSI:${stats.rsi}`;
  document.getElementById('realtime-stats').innerHTML = html;
}
/* 重大新闻/事件流 */
function renderNewsList() {
  const el = document.getElementById('news-list');
  el.innerHTML = '';
  newsData.forEach(news => {
    const div = document.createElement('div');
    div.className = 'news-item';
    div.setAttribute('data-type', news.type);
    div.innerHTML = `<div>
      <b>[${news.type}]</b> ${news.title}
      <div class="news-meta">${news.time} | ${news.detail}</div>
    </div>`;
    el.appendChild(div);
  });
}
/* 投资组合表现 */
function renderPortfolio() {
  const canvas = document.getElementById('portfolio-canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;
  ctx.clearRect(0,0,canvas.width,canvas.height);
  // 饼图-资产配置
  let total = portfolioData.allocation.reduce((s,a)=>s+a.percent,0);
  let cx=canvas.width/2-30, cy=canvas.height/2, r=65;
  let start=0;
  portfolioData.allocation.forEach(a=>{
    let angle = a.percent/total*Math.PI*2;
    ctx.beginPath();
    ctx.moveTo(cx,cy);
    ctx.arc(cx,cy,r,start,start+angle,false);
    ctx.closePath();
    ctx.fillStyle = a.color;
    ctx.globalAlpha = 0.84;
    ctx.fill();
    ctx.globalAlpha = 1;
    start+=angle;
  });
  // 图例
  portfolioData.allocation.forEach((a,i)=>{
    ctx.fillStyle = a.color;
    ctx.fillRect(canvas.width-140, 32+i*28, 20, 20);
    ctx.fillStyle = "#fff";
    ctx.font = "18px Segoe UI";
    ctx.fillText(`${a.name} ${a.percent}%`, canvas.width-110, 48+i*28);
  });
  // 盈亏柱状图
  let basey = cy+60, barw=28;
  for(let i=0;i<portfolioData.profit.length;i++){
    let p = portfolioData.profit[i];
    let x = 36+i*barw*1.7;
    let y = basey;
    let h = p.pnl*3;
    ctx.fillStyle = p.pnl>0?'#16e79a':'#fd5061';
    ctx.fillRect(x, y, barw, -h);
    ctx.font = "15px Segoe UI";
    ctx.fillStyle = "#c0ccfa";
    ctx.fillText(p.name, x-4, y+18);
  }
  // 总收益
  document.getElementById('portfolio-stats').innerHTML =
    `<div class="portfolio-stat">总收益率 <span>${portfolioData.totalReturn}%</span></div>
    <div class="portfolio-stat">Beta <span>${portfolioData.beta}</span></div>
    <div class="portfolio-stat">VaR <span>${portfolioData.varRisk}%</span></div>`;
}
/* AI风险预警 */
function renderAIAlerts() {
  const el = document.getElementById('ai-alerts');
  el.innerHTML = '';
  if(!aiAlerts.length) {
    el.innerHTML = `<div class="ai-alert info">当前无重大风险预警</div>`;
    return;
  }
  aiAlerts.forEach(alert=>{
    const div = document.createElement('div');
    div.className = "ai-alert " + alert.level;
    div.innerHTML = alert.msg;
    el.appendChild(div);
  });
}
/* KPI对比 */
function renderKPI() {
  const canvas = document.getElementById('kpi-canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;
  ctx.clearRect(0,0,canvas.width,canvas.height);
  let N = kpiData.length;
  let min = Math.min(...kpiData), max = Math.max(...kpiData);
  // 线图
  ctx.strokeStyle = "#1be9b3";
  ctx.lineWidth = 3;
  ctx.beginPath();
  for(let i=0;i<N;i++){
    let x = (i/(N-1))*(canvas.width-80)+38;
    let y = (1-(kpiData[i]-min)/(max-min+0.01))*(canvas.height-40)+12;
    if(i===0) ctx.moveTo(x,y);
    else ctx.lineTo(x,y);
  }
  ctx.stroke();
  // 坐标轴
  ctx.strokeStyle = "#343c50";
  ctx.lineWidth = 1;
  ctx.beginPath();
  ctx.moveTo(40,10); ctx.lineTo(40,canvas.height-18); ctx.lineTo(canvas.width-18,canvas.height-18);
  ctx.stroke();
  // 标注
  ctx.fillStyle = "#fff";
  ctx.font = "17px Segoe UI";
  ctx.fillText("本月", canvas.width-80, canvas.height-28);
  ctx.fillText("12期前", 44, canvas.height-28);
  ctx.fillStyle="#b9bfcf";
  ctx.fillText("最大", 4, 24); ctx.fillText("最小", 4, canvas.height-18);
}
document.getElementById('kpi-select').addEventListener('change', e=>{
  kpiType = e.target.value;
  kpiData = generateKPIData(kpiType);
  renderKPI();
});
/* 自选股仪表盘 */
function renderWatchlist() {
  const el = document.getElementById('watchlist-table');
  el.innerHTML = `<tr>
    <th>股票</th><th>现价</th><th>涨跌幅</th><th>PE</th><th>PB</th>
    <th>AI评级</th><th>预警</th>
  </tr>`;
  watchlistData.forEach(item=>{
    el.innerHTML += `<tr>
      <td>${item.name}<br>${item.code}</td>
      <td>${item.price}</td>
      <td class="${item.chg>0?'up':item.chg<0?'down':'neutral'}">${item.chg>0?'+':''}${item.chg}%</td>
      <td>${item.pe}</td><td>${item.pb}</td>
      <td><span class="ai-rating ${item.ai_rating}">${item.ai_rating==='buy'?'买入':item.ai_rating==='hold'?'观望':'卖出'}</span></td>
      <td>${item.warn?'<span class="ai-warning">'+item.warn+'</span>':'--'}</td>
    </tr>`;
  });
}
/* 股价/成交量趋势图（分时/K线+指标） */
let trendSymbol = symbols[0].code;
let trendPeriod = '1d';
document.getElementById('trend-symbol').onchange = function(e){
  trendSymbol = e.target.value;
  renderTrend();
};
document.getElementById('trend-period').onchange = function(e){
  trendPeriod = e.target.value;
  trendData[trendSymbol] = generateTrendData(trendSymbol,trendPeriod);
  renderTrend();
};
['ma-ind','macd-ind','rsi-ind'].forEach(id=>{
  document.getElementById(id).onchange = renderTrend;
});
// 初始化symbol下拉
(function(){
  let sel = document.getElementById('trend-symbol');
  let sel2 = document.getElementById('ai-predict-symbol');
  symbols.forEach(s=>{
    sel.innerHTML += `<option value="${s.code}">${s.name}（${s.code}）</option>`;
    sel2.innerHTML += `<option value="${s.code}">${s.name}（${s.code}）</option>`;
  });
  sel.value = trendSymbol;
  sel2.value = trendSymbol;
})();
function renderTrend() {
  const canvas = document.getElementById('trend-canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;
  ctx.clearRect(0,0,canvas.width,canvas.height);
  let t = trendData[trendSymbol];
  let N = t.length;
  let min = Math.min(...t.map(x=>x.low)), max = Math.max(...t.map(x=>x.high));
  // K线图
  let left=55, right=32, top=20, bottom=60;
  let W=canvas.width-left-right, H=canvas.height-top-bottom;
  // 坐标轴
  ctx.strokeStyle = "#343c50";
  ctx.lineWidth = 1.2;
  ctx.beginPath();
  ctx.moveTo(left,top); ctx.lineTo(left,canvas.height-bottom);
  ctx.lineTo(canvas.width-right,canvas.height-bottom);
  ctx.stroke();
  // K线
  let barw = Math.max(5, W/N-3.2);
  for(let i=0;i<N;i++){
    let x = left+i*W/(N-1);
    let o = t[i].open, c = t[i].close, h = t[i].high, l = t[i].low;
    let yO = top+(1-(o-min)/(max-min))*H;
    let yC = top+(1-(c-min)/(max-min))*H;
    let yH = top+(1-(h-min)/(max-min))*H;
    let yL = top+(1-(l-min)/(max-min))*H;
    ctx.strokeStyle = c>=o? "var(--k-green)":"var(--k-red)";
    ctx.lineWidth = 2.1;
    ctx.beginPath();
    ctx.moveTo(x, yH); ctx.lineTo(x, yL);
    ctx.stroke();
    ctx.lineWidth = barw;
    ctx.beginPath();
    ctx.moveTo(x, yO); ctx.lineTo(x, yC);
    ctx.stroke();
  }
  // 成交量柱状图
  let volMax = Math.max(...t.map(x=>x.vol));
  let volH = 38;
  for(let i=0;i<N;i++){
    let x = left+i*W/(N-1);
    let v = t[i].vol;
    let y = canvas.height-bottom+volH;
    let h = -v/volMax*volH;
    ctx.fillStyle = "rgba(80,180,250,0.16)";
    ctx.fillRect(x-barw/2, y, barw, h);
  }
  // MA
  if(document.getElementById('ma-ind').checked){
    ctx.strokeStyle = "#f9c846";
    ctx.lineWidth = 2.2;
    ctx.beginPath();
    for(let i=0;i<N;i++){
      let s = Math.max(0,i-5), arr=t.slice(s,i+1);
      let ma = arr.map(x=>x.close).reduce((a,b)=>a+b,0)/arr.length;
      let y = top+(1-(ma-min)/(max-min))*H;
      let x0 = left+i*W/(N-1);
      if(i===0) ctx.moveTo(x0,y);
      else ctx.lineTo(x0,y);
    }
    ctx.stroke();
  }
  // MACD
  if(document.getElementById('macd-ind').checked){
    ctx.strokeStyle = "#e04e39";
    ctx.lineWidth = 1.7;
    ctx.beginPath();
    for(let i=0;i<N;i++){
      let s5 = Math.max(0,i-5), s10=Math.max(0,i-10);
      let ma5 = t.slice(s5,i+1).map(x=>x.close).reduce((a,b)=>a+b,0)/(i-s5+1);
      let ma10 = t.slice(s10,i+1).map(x=>x.close).reduce((a,b)=>a+b,0)/(i-s10+1);
      let dif = ma5-ma10;
      let y = top+(1-(dif+10)/(20))*H;
      let x0 = left+i*W/(N-1);
      if(i===0) ctx.moveTo(x0,y);
      else ctx.lineTo(x0,y);
    }
    ctx.stroke();
  }
  // RSI
  if(document.getElementById('rsi-ind').checked){
    ctx.strokeStyle = "#56aaff";
    ctx.lineWidth = 1.6;
    ctx.beginPath();
    for(let i=6;i<N;i++){
      let up=0,down=0;
      for(let j=i-6;j<i;j++){
        let v = t[j+1].close-t[j].close;
        if(v>0) up+=v; else down-=v;
      }
      let rsi = up/(up+down+0.01)*100;
      let y = top+(1-rsi/100)*H;
      let x0 = left+i*W/(N-1);
      if(i===6) ctx.moveTo(x0,y);
      else ctx.lineTo(x0,y);
    }
    ctx.stroke();
  }
  // 标注
  ctx.font = "20px Segoe UI";
  ctx.fillStyle="#fff";
  ctx.fillText(symbols.find(s=>s.code===trendSymbol).name, left+6, top+22);
  ctx.fillStyle="#b9bfcf";
  ctx.font = "16px Segoe UI";
  ctx.fillText("高", canvas.width-right-30, top+16);
  ctx.fillText("低", canvas.width-right-30, canvas.height-bottom-5);
}
/* AI预测分析面板 */
let aiPredictSymbol = symbols[0].code;
let aiPredictHorizon = '1d';
document.getElementById('ai-predict-symbol').onchange = function(e){
  aiPredictSymbol = e.target.value;
  renderAIPredict();
};
document.getElementById('ai-predict-horizon').onchange = function(e){
  aiPredictHorizon = e.target.value;
  renderAIPredict();
};
function renderAIPredict() {
  const canvas = document.getElementById('ai-predict-canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;
  ctx.clearRect(0,0,canvas.width,canvas.height);
  aiPredictData = generateAIPredict(aiPredictSymbol, aiPredictHorizon);
  // 区间
  let min = aiPredictData.low, max = aiPredictData.high;
  let now = aiPredictData.now;
  let left = 70, right=50, top=24, bottom=36;
  let W=canvas.width-left-right, H=canvas.height-top-bottom;
  // 轴
  ctx.strokeStyle="#232835";
  ctx.beginPath();
  ctx.moveTo(left,top); ctx.lineTo(left,canvas.height-bottom);
  ctx.lineTo(canvas.width-right,canvas.height-bottom);
  ctx.stroke();
  // 当前价点
  let x0 = left+0.2*W;
  let y0 = top+(1-(now-min)/(max-min))*H;
  ctx.beginPath();
  ctx.arc(x0, y0, 9, 0, 2*Math.PI);
  ctx.fillStyle="#56aaff";
  ctx.globalAlpha=0.77;
  ctx.fill();
  ctx.globalAlpha=1;
  ctx.font = "18px Segoe UI";
  ctx.fillStyle="#fff";
  ctx.fillText("当前", x0-28, y0-12);
  // 预测区间
  let x1 = left+0.6*W;
  ctx.strokeStyle = "#f9c846";
  ctx.lineWidth = 13;
  ctx.beginPath();
  let yLow = top+(1-(aiPredictData.low-min)/(max-min))*H;
  let yHigh = top+(1-(aiPredictData.high-min)/(max-min))*H;
  ctx.moveTo(x1, yLow);
  ctx.lineTo(x1, yHigh);
  ctx.stroke();
  // 预测涨跌概率
  ctx.font = "bold 32px Segoe UI";
  ctx.fillStyle = aiPredictData.probUp>0.55?"#16e79a":aiPredictData.probUp<0.45?"#e04e39":"#f9c846";
  ctx.fillText(
    (aiPredictData.probUp*100).toFixed(1)+"%概率上涨",
    canvas.width-320, top+44
  );
  // 标注
  ctx.font = "18px Segoe UI";
  ctx.fillStyle="#fff";
  ctx.fillText(`未来${aiPredictHorizon==='1h'?'1小时':aiPredictHorizon==='1d'?'1天':'1周'}预测区间`, x1-16, yHigh+22);
}
/* ==== 交互、刷新、搜索等 ==== */
/* 定时刷新所有模块 */
function doRefresh() {
  heatmapData = generateHeatmapData();
  newsData = generateNewsList();
  portfolioData = generatePortfolio();
  kpiData = generateKPIData(kpiType);
  aiAlerts = generateAIAlerts();
  watchlistData = generateWatchlistData();
  // 随机波动trend
  symbols.forEach(s => trendData[s.code] = generateTrendData(s.code,trendPeriod));
  renderAll();
}
function renderAll() {
  updateHeader();
  renderHeatmap();
  renderRealtime();
  renderNewsList();
  renderPortfolio();
  renderAIAlerts();
  renderKPI();
  renderTrend();
  renderAIPredict();
  renderWatchlist();
}
function startRefreshTimer() {
  if(refreshTimer) clearInterval(refreshTimer);
  refreshTimer = setInterval(doRefresh, refreshInterval*1000);
}
document.getElementById('refresh-interval').onchange = function(e){
  let v = +e.target.value;
  if(v<1||v>60) { e.target.value = refreshInterval; return;}
  refreshInterval = v;
  startRefreshTimer();
};
document.getElementById('refresh-btn').onclick = function(){
  doRefresh();
};
document.getElementById('add-watch-btn').onclick = function(){
  const v = document.getElementById('stock-search').value.trim();
  if(!v) return;
  let found = symbols.find(s=>s.code===v || s.name===v);
  if(!found) {
    alert('未找到该股票');
    return;
  }
  if(watchlistData.find(i=>i.code===found.code)) {
    alert('已在自选列表');
    return;
  }
  watchlistData.unshift({
    code:found.code, name:found.name, price:'--', chg:0, pe:0, pb:0, ai_rating:'hold', warn:''
  });
  renderWatchlist();
};
document.getElementById('search-btn').onclick = function(){
  const v = document.getElementById('stock-search').value.trim();
  if(!v) return;
  let found = symbols.find(s=>s.code===v || s.name===v);
  if(!found) { alert('未找到该股票'); return;}
  trendSymbol = found.code;
  document.getElementById('trend-symbol').value = trendSymbol;
  renderTrend();
  aiPredictSymbol = found.code;
  document.getElementById('ai-predict-symbol').value = aiPredictSymbol;
  renderAIPredict();
};
/* 热力图点击交互：切换trendSymbol */
document.getElementById('heatmap-canvas').onclick = function(e){
  const rect = this.getBoundingClientRect();
  const x = e.clientX-rect.left, y=e.clientY-rect.top;
  // 行列定位
  const M = heatmapData.length;
  const rows = sectors.length;
  const cols = Math.ceil(M/rows);
  const cellW = (this.width-60)/(cols), cellH = (this.height-30)/(rows);
  let c = Math.floor((x-30)/cellW), r = Math.floor((y-20)/cellH);
  let idx = r*cols+c;
  if(idx>=0 && idx<M) {
    let code = heatmapData[idx].code;
    trendSymbol = code;
    document.getElementById('trend-symbol').value = code;
    renderTrend();
    aiPredictSymbol = code;
    document.getElementById('ai-predict-symbol').value = code;
    renderAIPredict();
  }
};
/* 趋势图点击：显示详细数据 */
document.getElementById('trend-canvas').onclick = function(e){
  const canvas = this;
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX-rect.left;
  const t = trendData[trendSymbol];
  let left=55, right=32, W=canvas.width-left-right;
  let N = t.length;
  let idx = Math.floor((x-left)/W*N);
  if(idx<0||idx>=N) return;
  let d = t[idx];
  alert(`${symbols.find(s=>s.code===trendSymbol).name}\n开盘:${d.open.toFixed(2)}\n收盘:${d.close.toFixed(2)}\n最高:${d.high.toFixed(2)}\n最低:${d.low.toFixed(2)}\n成交量:${d.vol}`);
};
/* ===== 启动 ===== */
renderAll();
setInterval(updateHeader, 1000);
startRefreshTimer();
</script>
</body>
</html>